﻿using System;

namespace MathService.Values
{
    /// <summary>Усредняемая по времени величина</summary>
    [Serializable]
    public class TimeAverageValue : IValue<double>, IResetable
    {
        /// <summary>Последнее время доступа</summary>
        protected TimeSpan f_LastTime;

        /// <summary>Значение</summary>
        protected double f_LastValue;

        /// <summary>Постоянная времени</summary>
        public double Tau { get; set; }

        /// <summary>Значение</summary>
        public double Value { get { return f_LastValue; } set { f_LastValue = value; } }

        /// <summary>Признак инициализации</summary>
        public bool Initialized { get; protected set; }

        /// <summary>Усредняемая по времени величина</summary>
        public TimeAverageValue()
        {
            Initialized = false;
        }

        /// <summary>Усредняемая по времени величина</summary>
        /// <param name="tau">Постоянная времени усреднения</param>
        public TimeAverageValue(double tau)
        {
            Initialized = false;
            Tau = tau;
        }

        /// <summary>Усредняемая по времени величина</summary>
        /// <param name="Value">Начальное значение</param>
        /// <param name="tau">постоянная времени</param>
        public TimeAverageValue(double Value, double tau)
        {
            Tau = tau;

            f_LastValue = Value;
            f_LastTime = DateTime.Now.TimeOfDay;
            Initialized = true;
        }

        /// <summary>Добавить значение</summary>
        /// <param name="value">Значение</param>
        public virtual double Add(double value)
        {
            var lv_CurrentTime = DateTime.Now.TimeOfDay;
            if(!Initialized || Math.Abs(Tau) < double.Epsilon)
            {
                Initialized = true;
                f_LastValue = value;
                f_LastTime = lv_CurrentTime;
                return value;
            }

            var lv_Delta = value - f_LastValue;
            var dt = (lv_CurrentTime - f_LastTime).TotalSeconds;
            f_LastTime = lv_CurrentTime;
            if(Math.Abs(lv_Delta) < double.Epsilon) return f_LastValue;
            f_LastValue += lv_Delta * dt / Tau;
            return f_LastValue;
        }

        /// <summary>Сброс состояния</summary>
        public void Reset() { Initialized = false; }

    }
}
